import time
import pandas as pd
import collections
import logging

from xunkemgmt_client.client.util.query_util import query_exchange_apis


_KlineRequest = collections.namedtuple('_KlineRequest', [
  'market_type',
  'exchange',
  'symbol_contract_type',
  'kline_period',
  'source_exchange_api_id',
  'trading_date',
  'query_timestamp',
  ])


class KlineRequest(_KlineRequest):
  def __new__(cls, market_type=None, exchange=None, symbol_contract_type=None, kline_period=None,
              source_exchange_api_id=None, trading_date=None, query_timestamp=None):
    assert market_type in ('Spot', 'Futures', 'Info', 'Options'), market_type
    assert exchange is not None, exchange
    return super(KlineRequest, cls).__new__(
        cls, market_type, exchange, symbol_contract_type, kline_period, source_exchange_api_id, trading_date, query_timestamp)


def drop_duplicate_kline_request(kline_request_list):
  seen_values = []
  new_request_list = []
  for kline_request in kline_request_list:
    request_value = list(kline_request._asdict().values())
    request_value = request_value[:-1]
    if request_value not in seen_values:
      new_request_list.append(kline_request)
      seen_values.append(request_value)
  return new_request_list


def _get_api_version_for_request(market_type, exchange, symbol):
  if symbol == 'TOTAL':
    api_version = 'coingecko'
  elif market_type == 'Futures' and exchange == 'Binance':
    if '-USD.' in symbol:
      api_version = 'v1-delivery'
    else:
      api_version = 'v1'
  elif market_type == 'Spot' and exchange == 'Binance':
    api_version = 'v1'
  elif market_type == 'Futures' and exchange == 'Huobi':
    if 'PERPETUAL' in symbol:
      if '-USDT.' in symbol:
        api_version = 'v1-linear-swap'
      else:
        api_version = 'v1-swap'
    else:
      api_version = 'v1'
  elif market_type == 'Spot' and exchange == 'Huobi':
    api_version = 'v1'
  elif market_type == 'Futures' and exchange == 'Okex':
    if 'PERPETUAL' in symbol:
      api_version = 'v5-swap'
    else:
      api_version = 'v5'
  elif market_type == 'Spot' and exchange == 'Okex':
    api_version = 'v5'
  elif market_type == 'Futures' and exchange == 'Ftx':
    api_version = 'v1'
  elif market_type == 'Spot' and exchange == 'Ftx':
    api_version = 'v1'
  elif market_type == 'Spot' and exchange == 'Gdax':
    api_version = 'v1'
  elif market_type == 'Spot' and exchange == 'Kraken':
    api_version = 'v1'
  elif market_type == 'Spot' and exchange == 'Kucoin':
    api_version = 'v1'
  elif market_type == 'Spot' and exchange == 'Upbit':
    api_version = 'v1'
  elif market_type == 'Spot' and exchange == 'Upbitid':
    api_version = 'v1'
  elif market_type == 'Spot' and exchange == 'Bitbank':
    api_version = 'v1'
  elif market_type == 'Spot' and exchange == 'Bithumb':
    api_version = 'v2'
  elif market_type == 'Spot' and exchange == 'Bybit':
    api_version = 'v1'
  elif market_type == 'Futures' and exchange == 'Bybit':
    if '-USDT.' in symbol:
      api_version = 'v3-linear'
    elif '-USDC.' in symbol:
      api_version = 'v3-linear-usdc'
    else:
      api_version = 'v3'
  elif market_type == 'Futures' and exchange == 'Dydx':
    api_version = 'v3'
  elif exchange == 'Deribit':
    api_version = 'v2'
  elif market_type == 'Spot' and exchange == 'Gateio':
    api_version = 'v4'
  elif market_type == 'Spot' and exchange == 'Mexc':
    api_version = 'v2'
  elif market_type == 'Spot' and exchange == 'Korbit':
    api_version = 'v1'
  elif market_type == 'Spot' and exchange == 'Btcturk':
    api_version = 'v1'
  elif market_type == 'Futures' and exchange == 'Phemex':
    if '-USDT.' in symbol:
      api_version = 'v1-linear'
    else:
      api_version = 'v1'
  elif market_type == 'Spot' and exchange == 'Phemex':
    api_version = 'v1'
  elif market_type == 'Futures' and exchange == 'Kucoin':
    api_version = 'v1'
  else:
    raise NotImplementedError("Not supported (%s, %s, %s)" % (market_type, exchange, symbol))
  return api_version


def get_kline_requests(trading_date, kline_symbols_list):
  result_list = []
  kline_collector_symbols = pd.read_csv(kline_symbols_list)
  exchange_api_df = query_exchange_apis().set_index(['market_type', 'exchange', 'api_version'])
  for i in range(len(kline_collector_symbols)):
    try:
      d = kline_collector_symbols.iloc[i].to_dict()
      market_type = d['market_type']
      exchange = d['exchange']
      symbol_contract_type = d['symbol']
      kline_period = d['kline_period']
      if 'api_version' not in d:
        request_list = query_exchange_apis(as_proto=True, market_types=[market_type], exchanges=[exchange])
        api_version_list = [request.api_version for request in request_list]
        api_version = _get_api_version_for_request(market_type, exchange, symbol_contract_type)
        if symbol_contract_type.lower() != 'total':
          assert api_version in api_version_list
        else:
          assert api_version.lower() == 'coingecko'
      else:
        api_version = d['api_version']
      if (market_type, exchange, api_version) in exchange_api_df.index:
        source_exchange_api_id = exchange_api_df.loc[(market_type, exchange, api_version), 'exchange_api_id']
      elif api_version == 'coingecko':
        source_exchange_api_id = exchange_api_df.loc[('Info', 'CoinGecko', api_version), 'exchange_api_id']
      else:
        raise ValueError("Not in exchange_api (%s, %s, %s)" % (market_type, exchange, api_version))
    except Exception as e:
      logging.error('Failed to generate kline request %s' % e)
      continue
    result_list.append(KlineRequest(
      market_type, exchange, symbol_contract_type, kline_period, source_exchange_api_id, trading_date, time.time()))
  result_list = drop_duplicate_kline_request(result_list)
  return result_list


def select_kline_request(
    request_list: list,
    *,
    market_type=None,
    exchange=None,
    kline_period=None,
    source_exchange_api_id=None):
  result_list = request_list
  if market_type:
    market_type_list = [elem.strip() for elem in market_type.split(',')]
    result_list = [
        request for request in result_list if isinstance(
        request, KlineRequest) and request.market_type in market_type_list]
  if exchange:
    exchange_list = [elem.strip() for elem in exchange.split(',')]
    result_list = [
        request for request in result_list if isinstance(
        request, KlineRequest) and request.exchange in exchange_list]
  if kline_period:
    kline_period_list = [elem.strip() for elem in kline_period.split(',')]
    result_list = [
        request for request in result_list if isinstance(
        request, KlineRequest) and request.kline_period in kline_period_list]
  if source_exchange_api_id:
    kline_source_exchange_api_id_list = [int(elem.strip()) for elem in source_exchange_api_id.split(',')]
    result_list = [
        request for request in result_list if isinstance(
        request, KlineRequest) and request.source_exchange_api_id in kline_source_exchange_api_id_list]
  return result_list
